package remato.client.view.session;


import java.lang.reflect.InvocationTargetException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.prefs.BackingStoreException;
import java.util.prefs.Preferences;

import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.dialogs.ErrorDialog;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.IInputValidator;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.window.IShellProvider;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.progress.IProgressService;

import remato.client.controller.Assert;
import remato.client.controller.ModelStatus;
import remato.client.controller.PropertyAccessor;
import remato.client.controller.Registry;
import remato.client.controller.RequirementPlugin;
import remato.client.view.util.ReflectionHelper;
import remato.client.view.util.ReflectionRunnableWithProgress;
import remato.client.view.util.validators.UrlInputValidator;
import remato.domain.common.RequirementSystem;

import com.wgo.bpot.client.session.IncompatibleClientVersionException;
import com.wgo.bpot.common.transport.exception.RematoException;
import com.wgo.precise.client.common.session.ISessionHolder;
import com.wgo.precise.client.common.session.PreciseSession;
import com.wgo.precise.client.common.session.PreciseSessionImpl;

/**
 * 
 * @author Petter L. H. Eide
 * @version $Id: LoginDialog.java,v 1.1 2006-01-23 19:58:36 petterei Exp $
 */
public class LoginDialog extends Dialog {

    private static final int RESET_ID = IDialogConstants.NO_TO_ALL_ID + 1;

	private static final String SERVER_ADDRESS = "/precise/serverAddress";
	private static final String USER_NAME = 	 "/precise/userName";

    private Text usernameField;
    private Text passwordField;
    private Text serverAdressField;
    
    private ISessionHolder sessionHolder;

    private String text;
    private String message;

    private IInputValidator validator = new UrlInputValidator("Invalid server address:");
    
    private PreciseSession session;
    /**
     * Error message label widget.
     */
    private Text errorMessageText;

    public LoginDialog(Shell parentShell, ISessionHolder newSessionHolder) {
        this(parentShell, newSessionHolder, PropertyAccessor.getLocalString("login.window.title"), PropertyAccessor.getLocalString("login.window.tooltiptext"));
    }
    
    public LoginDialog(Shell parentShell, ISessionHolder newSessionHolder, String text, String dialogMessage) {
        super(parentShell);
        Assert.notNull(newSessionHolder, ISessionHolder.class.getSimpleName());
        sessionHolder = newSessionHolder;
        this.text = text;
        this.message = dialogMessage;
    }

    /**
     * @param parentShell
     */
    public LoginDialog(IShellProvider parentShell, ISessionHolder newSessionHolder) {
        super(parentShell);
        Assert.notNull(newSessionHolder, ISessionHolder.class.getSimpleName());
        sessionHolder = newSessionHolder;
        text = PropertyAccessor.getLocalString("login.window.title");
        message = PropertyAccessor.getLocalString("login.window.tooltiptext");
    }
    
    /**
     * @see org.eclipse.jface.dialogs.Dialog#createDialogArea(org.eclipse.swt.widgets.Composite)
     */
    @Override
    protected Control createDialogArea(Composite parent) {
    	Shell shell = getShell();
    	shell.setText(text);
    	shell.setToolTipText(message);
    	
    	Composite composite = (Composite) super.createDialogArea(parent);
    	
        GridLayout layout= (GridLayout) composite.getLayout();

        layout.numColumns = 2;
        
        GridData data;
        // create message
        if (message != null) {
            Label label = new Label(composite, SWT.WRAP);
            label.setText(message);
            data = new GridData(GridData.GRAB_HORIZONTAL
                    | GridData.GRAB_VERTICAL | GridData.HORIZONTAL_ALIGN_FILL
                    | GridData.VERTICAL_ALIGN_CENTER);
            data.widthHint = convertHorizontalDLUsToPixels(IDialogConstants.MINIMUM_MESSAGE_AREA_WIDTH);
            data.horizontalSpan = 2;
            label.setLayoutData(data);
            label.setFont(parent.getFont());
        }
        
        Label usernameLabel = new Label(composite, SWT.RIGHT);
        usernameLabel.setText("Username: ");
        
        usernameField = new Text(composite, SWT.SINGLE);
        data = new GridData(GridData.FILL_HORIZONTAL);
        usernameField.setLayoutData(data);
        usernameField.setText(getUserName());
        
        Label passwordLabel = new Label(composite, SWT.RIGHT);
        passwordLabel.setText("Password: ");
        
        passwordField = new Text(composite, SWT.SINGLE | SWT.PASSWORD);
        data = new GridData(GridData.FILL_HORIZONTAL);
        passwordField.setLayoutData(data);

        Label serverAdressLabel = new Label(composite, SWT.RIGHT);
        serverAdressLabel.setText("Server address: ");
        
        serverAdressField = new Text(composite, SWT.SINGLE);
        data = new GridData(GridData.FILL_HORIZONTAL);
        serverAdressField.setLayoutData(data);
        serverAdressField.setText(getServerAddress());//"http://" + Registry.DEFAULT_SERVER_ADRESS + ":8080/services");
        serverAdressField.addModifyListener(new ModifyListener() {
            public void modifyText(ModifyEvent e) {
                validateInput();
            }
        });
        errorMessageText = new Text(composite, SWT.READ_ONLY);
        data = new GridData(GridData.GRAB_HORIZONTAL | GridData.HORIZONTAL_ALIGN_FILL);
        data.horizontalSpan = 2;
        errorMessageText.setLayoutData(data);
        errorMessageText.setBackground(errorMessageText.getDisplay().getSystemColor(SWT.COLOR_WIDGET_BACKGROUND));
        applyDialogFont(composite);
        return composite;
    }
    
    private static String getServerAddress(){
    	Preferences prefs = Preferences.userRoot();
    	return prefs.get(SERVER_ADDRESS,"http://localhost:8080/bpot-web");
    }

    private static String getUserName(){
    	Preferences prefs = Preferences.userRoot();
    	return prefs.get(USER_NAME,"plheide");
    }

    /**
     * @see org.eclipse.jface.dialogs.Dialog#buttonPressed(int)
     */
    @Override
    protected void buttonPressed(int buttonId) {
        if(buttonId == RESET_ID) {
            usernameField.setText("");
            passwordField.setText("");
        } else {
            super.buttonPressed(buttonId);
        }
    }

    /**
     * @see org.eclipse.jface.dialogs.Dialog#createButtonsForButtonBar(org.eclipse.swt.widgets.Composite)
     */
    @Override
    protected void createButtonsForButtonBar(Composite parent) {
        super.createButtonsForButtonBar(parent);
        createButton(parent, RESET_ID, "Reset All", false);
    }

    /**
     * @see org.eclipse.jface.dialogs.Dialog#okPressed()
     */
    @Override
    protected void okPressed() {
    	Preferences prefs = Preferences.userRoot();
    	prefs.put(SERVER_ADDRESS,serverAdressField.getText());
    	prefs.put(USER_NAME,usernameField.getText());
        IRunnableWithProgress op = new ReflectionRunnableWithProgress("Loggin on to " + serverAdressField.getText(), ReflectionHelper.retrieveMethod(getClass().getSimpleName(), getClass(), "login"),this);
		IProgressService progressService = PlatformUI.getWorkbench().getProgressService();
		try {
			progressService.runInUI(PlatformUI.getWorkbench().getProgressService(), op, null);
		} catch (InvocationTargetException ite) {
			Throwable e = ite;
			while (e instanceof InvocationTargetException) {
				e = ((InvocationTargetException)e).getTargetException();
			}
			loginFailed(e.getMessage(), e);
		} catch (IncompatibleClientVersionException e) {
			RequirementPlugin.logAndDisplay(new ModelStatus(IStatus.ERROR, "The client could not connect to the server. Please update client.", e));
		} catch (RematoException e) {
			loginFailed(e.getMessage(), e);
		} catch (InterruptedException e) {
			loginFailed(e.getMessage(), e);
		} catch (Exception e) {
			loginFailed("Could not connect to server.", e);
		} 
        sessionHolder.setSession(session);
    	try {
			prefs.flush();
		} catch (BackingStoreException e) {
			RequirementPlugin.log(this.getClass(),"Unable to store the server address. Backing store exception: " + e.getMessage());
		}
        super.okPressed();
    }

	public synchronized void login () throws MalformedURLException {
		RequirementPlugin.log(getClass(), "Logging onto server: " + serverAdressField.getText());
		URL url = new URL(serverAdressField.getText());
//			domainSession = new LightSession(url, Registry.CLIENT_VERSION); //FIXME: where did client version go..
//			domainSession = new LightSession(url, null);
		session = new PreciseSessionImpl(url, RequirementSystem.class);
	}
	
	private void loginFailed(String message, Throwable e) {
		IStatus status = new Status (IStatus.ERROR, Registry.PLUGIN_ID, 0, message , e);
		ErrorDialog.openError(getParentShell(), "Loggin on to " + serverAdressField.getText() + "failed", message, status);
		RequirementPlugin.getInstance().getLog().log( status );
	}

	/* (non-Javadoc)
	 * @see org.eclipse.jface.window.Window#open()
	 */
	@Override
	public int open() {
		if (sessionHolder.isSessionSet()){
			sessionHolder.disposeSession();
		}
		return super.open();
	}

    /**
     * To set focus right.
     * 
     * @see org.eclipse.jface.window.Window#open()
     */
//    @Override
//    public int open() {
//        int i;
//        i = super.open();
//        usernameField.setFocus();
//        return i;
//    }

    /**
     * Validates the input.
     * <p>
     * The default implementation of this framework method delegates the request
     * to the supplied input validator object; if it finds the input invalid,
     * the error message is displayed in the dialog's message line. This hook
     * method is called whenever the text changes in the input field.
     * </p>
     */
    protected void validateInput() {
        String errorMessage = null;
        if (validator != null) {
            errorMessage = validator.isValid(serverAdressField.getText());
        }
        // Bug 16256: important not to treat "" (blank error) the same as null
        // (no error)
        setErrorMessage(errorMessage);
    }

    /**
     * Sets or clears the error message.
     * If not <code>null</code>, the OK button is disabled.
     * 
     * @param errorMessage
     *            the error message, or <code>null</code> to clear
     */
    public void setErrorMessage(String errorMessage) {
        errorMessageText.setText(errorMessage == null ? "" : errorMessage); //$NON-NLS-1$
        getButton(IDialogConstants.OK_ID).setEnabled(errorMessage == null);
        errorMessageText.getParent().update();
    }
}
